package java_thinking.unit_9._9_3;

/**
 * @Description : 策略设计模式与适配设计模式的说明 https://www.cnblogs.com/tongye/p/6676039.html
 *
 *              策略模式强调的是做同一件事情的不同方法,这些方法不能重复,也就是正交多态只不过是一种语言机制
 *              ,有的不支持多态的语言一样也要实现策略策略处于设计层次,多态处于语言的层次
 * 策略算法：策略算法是相同行为的不同实现
 *
 * 策略模式的优点
 * 　　(1)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把
 * 公共的代码移到父类里面，从而避免代码重复。
 *
 * 　　(2)使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护，它把采取哪一种算法或采取哪
 * 一种行为的逻辑与算法或行为的逻辑混合在一起，统统列在一个多重条件语句里面，比使用继承的办法还要原始和落后。
 *
 * 策略模式的缺点
 * 　　(1)客户端必须知道所有的策略类，并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别，
 * 以便适时选择恰当的算法类。换言之，策略模式只适用于客户端知道算法或行为的情况。
 *
 * 　　(2)由于策略模式把每个具体的策略实现都单独封装成为类，如果备选的策略很多的话，那么对象的数目就会很可观。
 * @author: cww
 * @DateTime: 2019-03-04 22:01
 */

import java.util.Arrays;

/**
 * 用接口提高复用率
 */
interface Processor1 {
    /**
     * @return
     */
    String name();
    Object process(Object input);

    /**
     * 这个方法主要是在验证适配器时候做对比用的
     */
    void otherMethod();
}

/**
 * 1.这个例子用StringProcessor实现了Processor1接口，其主要目的是因为Upcase1、Downcase1、Splitter1
 * 这些类会统一用到Processor1.name()这个方法来获取当前实例对象的类名，这个属于继承的特性，满足复用
 * 的目的；
 * 2.而Apply1.process能够根据所传递的参数对象的不同而具有不同行为的方法，这就是策略设计模式
 */

abstract class StringProcessor implements Processor1{
    @Override
    public String name(){
        return getClass().getSimpleName();
    }

    /**
     * 可说明，引用接口方法，可以将普通方法引用为抽象方法
     * @param o
     * @return
     */
    @Override
    public abstract String process(Object o);

    @Override
    public void otherMethod() {
        System.out.println("other Method run");
    }
}
class Upcase1 extends StringProcessor{
    @Override
    public String process(Object o){
        //process覆写后都有各自的实现方法
        //向下转型返回字符串大写
        return ((String)o).toUpperCase();
    }
}

class Downcase1 extends StringProcessor {
    @Override
    public String process(Object o){
        return ((String)o).toLowerCase();
    }
}

class Splitter1 extends StringProcessor {
    @Override
    public String process(Object o){
        return Arrays.toString(((String)o).split(" "));
    }
}

class Apply1{
    public static void process(Processor1 p,Object s) {
        System.out.println("Using Processor "+p.name());
        System.out.println(p.process(s));
    }
}

class TextProcessor {
    public static String s = "If else and for each";

    public static void main(String[] args) {
        Apply1.process(new Upcase1(),s);
        Apply1.process(new Downcase1(),s);
        Apply1.process(new Splitter1(),s);
    }
}

/**
 * 适配器模式-对象适配器模式
 * 必看：http://blog.csdn.net/zhangjg_blog/article/details/18735243
 * 适配器模式将一个类的接口转换成客户期望的另一个接口，让原本不兼容的接口可以合作无间
 * 适配器模式的三个特点：
 *      1 适配器对象实现原有接口
 *      2 适配器对象组合一个实现新接口的对象(这个对象也可以不实现一个接口，只是一个单纯的对象)
 *      3 对适配器原有接口方法的调用被委托给新接口的实例的特定方法
 * 总结：按下面例子，我们就可以接受所有Filter，如果来了一个新的波形，我们只需要新建一个波形类继承与filter，
 * 就可以直接用FilterAdapter适配器，得到name方法以及process执行后想要的结果了，因此代码更具复用性
 * 见java_thinking.unit_17._17_2_6.CollectionData 另一种适配写法
 */

class FilterAdapter implements Processor1{
    /**
     * 源类，声明Filter引用
     */
    Filter filter;

    /**
     * 1.构造方法接收一个引用作为参数
     *
     * @param filter
     */
    public FilterAdapter(Filter filter){
        this.filter = filter;
    }

    /**
     * 在源Filter类中有name()、process()，这里直接委派
     * @return
     */
    @Override
    public String name(){
        return filter.name();
    }
    @Override
    public Waveform process(Object input){
        return filter.process((Waveform)input);
    }
    @Override
    public void otherMethod(){
        System.out.println("FilterAdapter.otherMethod()");
    }
}

class FilterProcessor{
    /**
     * 这里是策略设计模式
     * @param args
     */
    public static void main(String[] args) {
        Waveform waveform = new Waveform();
        Apply1.process(new FilterAdapter(new LowPass(1.0)),waveform);
        Apply1.process(new FilterAdapter(new HighPass(2.0)),waveform);
        Apply1.process(new FilterAdapter(new BandPass(3.0)),waveform);
    }
}

/**
 * 现在要求Filter类实现Processor，但是Filter类又不能做改动，这时候就要用到
 * 适配器模式里面的-类适配器模式
 */
class FilterAdapterByClass extends Filter implements Processor1{
    @Override
    public Waveform process(Object input) {
        return (Waveform)input;
    }

    @Override
    public void otherMethod(){
        System.out.println("FilterAdapterByClass.otherMethod()");
    }
}
class FilterByClassProcessor{
    public static void main(String[] args) {
        FilterAdapterByClass adapter = new FilterAdapterByClass();
        adapter.otherMethod();
        adapter.process(new Waveform());
        adapter.name();
    }
}
